#pragma once

#include <queue>
#include <mutex>
#include <pthread.h>

// 命名空间，避免冲突
namespace Yohifo
{
#define DEF_SIZE 10

    template<class T>
    class BlockQueue
    {
    public:
        BlockQueue(size_t cap = DEF_SIZE)
            :_cap(cap)
        {
            // 初始化锁与条件变量
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_pro_cond, nullptr);
            pthread_cond_init(&_con_cond, nullptr);
        }

        ~BlockQueue()
        {
            // 销毁锁与条件变量
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_pro_cond);
            pthread_cond_destroy(&_con_cond);
        }

        // 生产数据（入队）
        void Push(const T& inData)
        {
            // 加锁
            pthread_mutex_lock(&_mtx);

            // 循环判断条件是否满足
            while(IsFull())
            {
                pthread_cond_wait(&_pro_cond, &_mtx);
            }

            _queue.push(inData);

            // 可以加策略唤醒，比如生产一半才唤醒消费者
            pthread_cond_signal(&_con_cond);

            pthread_mutex_unlock(&_mtx);
        }

        // 消费数据（出队）
        void Pop(T* outData)
        {
            // 加锁
            pthread_mutex_lock(&_mtx);

            // 循环判读条件是否满足
            while(IsEmpty())
            {
                pthread_cond_wait(&_con_cond, &_mtx);
            }

            *outData = _queue.front();
            _queue.pop();

            // 可以加策略唤醒，比如消费完后才唤醒生产者
            pthread_cond_signal(&_pro_cond);

            pthread_mutex_unlock(&_mtx);
        }

    private:
        // 判断是否为满
        bool IsFull()
        {
            return _queue.size() == _cap;
        }
        
        // 判断是否为空
        bool IsEmpty()
        {
            return _queue.empty();
        }

    private:
        std::queue<T> _queue;
        size_t _cap; // 阻塞队列的容量
        pthread_mutex_t _mtx; // 互斥锁
        pthread_cond_t _pro_cond; // 生产者条件变量
        pthread_cond_t _con_cond; // 消费者条件变量
    };
}